package column


import (
	"github.com/treelab/updatecolumn/models"
)

  func instanceType() int32 {
    return models.RecordReference
  }

  deleteColumnEdges(command: UpdateColumn_v2, o: CommonOptions) {
    const { column } = command;
    if (column.uid) {
      o.delQuads.push(
        ...this.dgraphHelperService.getRemoveEdgeQuads(column.uid!, [
          'foreignWorkspaceId',
          'foreignCoreId',
          'foreignTableId',
          'symmetricColumnId',
          Relation.HAS_FOREIGN_WORKSPACE,
          Relation.HAS_FOREIGN_CORE,
          Relation.HAS_FOREIGN_TABLE,
          Relation.HAS_SYMMETRIC_COLUMN,
          Relation.HAS_LOOKUP_COLUMN,
          Relation.HAS_ROLLUP_COLUMN,
        ]),
      );
    }
    return this;
  }

  public hasNothingToUpdate(command: UpdateColumn_v2) {
    const { column } = command;
    const recordReferenceColumnConfig = command.columnConfig as RecordReferenceColumnConfig;
    return (
      column.columnType === this.instanceType &&
      column.foreignCoreId === recordReferenceColumnConfig.foreignCoreId &&
      column.foreignTableId === recordReferenceColumnConfig.foreignTableId &&
      column.foreignWorkspaceId === recordReferenceColumnConfig.foreignWorkspaceId &&
      column.symmetricColumnId === recordReferenceColumnConfig.symmetricColumnId &&
      column.oneToOne === recordReferenceColumnConfig?.oneToOne
    );
  }

  async updateColumn(
    command: UpdateColumn_v2,
    o: CommonOptions,
    helper: { rows?: Row[]; columnCopy?: Column; referenceColumns?: Column[]; userGroup?: any },
  ) {
    const { column, columnConfig } = command;
    await super.updateColumn(command, o, helper);

    const { foreignWorkspaceId, foreignCoreId, foreignTableId, oneToOne } = columnConfig as RecordReferenceColumnConfig;

    column.foreignWorkspaceId = foreignWorkspaceId;
    column.foreignCoreId = foreignCoreId;
    column.foreignTableId = foreignTableId;
    column.oneToOne = oneToOne ? oneToOne : false;

    const foreignWorkspace = o!.ormTransaction.nodeFor(Workspace, { uid: foreignWorkspaceId });
    column.hasForeignWorkspace.add(foreignWorkspace);

    const foreignCore = o!.ormTransaction.nodeFor(Core, { uid: foreignCoreId });
    column.hasForeignCore.add(foreignCore);

    const foreignTable = o!.ormTransaction.nodeFor(Table, { uid: foreignTableId });
    column.hasForeignTable.add(foreignTable);
    return this;
  }

  async convertColumn(
    command: UpdateColumn_v2,
    o: CommonOptions,
    helper: {
      rows?: Row[];
      columnCopy?: Column;
      referenceColumns?: Column[];
      userGroup?: any;
      symmetricColumnMap: Map<string, string>;
    },
  ) {
    const columnConfig = command.columnConfig as RecordReferenceColumnConfig;

    const { column } = command;
    if (column.columnType === ColumnType.RECORD_REFERENCE) {
      if (
        columnConfig.foreignWorkspaceId === column.foreignWorkspaceId &&
        columnConfig.foreignCoreId === column.foreignCoreId &&
        columnConfig.foreignTableId === column.foreignTableId
      ) {
        // If it's a record reference, return the result immedietely if no config has changed.
        /* Update both the origin and symmetric column config if the user is just changing
        the one to one config. Don't remove the existing cells or column edges. */
        if (column.oneToOne !== columnConfig.oneToOne) {
          // Update the origin column config to new one to one
          this.updateColumn(command, o, helper);
          const symmetricColumn = column.hasSymmetricColumn.get();
          const recordReferenceColumnConfig: RecordReferenceColumnConfig = plainToClass(RecordReferenceColumnConfig, {
            ...(command.columnConfig as RecordReferenceColumnConfig),
            symmetricColumnId: symmetricColumn.length ? symmetricColumn[0].uid : column.uid,
          });
          const recordReferenceColumnUpdated = new ColumnConfigUpdated(
            command.workspaceId,
            command.coreId,
            command.tableId,
            command.column.uid!,
            recordReferenceColumnConfig,
            command._metadata,
          );
          o.events.addEvents(recordReferenceColumnUpdated);
        }
        return this;
      }
    }

    const fromColumn = _.cloneDeep(column);
    fromColumn.columnType = column.columnType;
    fromColumn.foreignWorkspaceId = column.foreignWorkspaceId;
    fromColumn.foreignCoreId = column.foreignCoreId;
    fromColumn.foreignTableId = column.foreignTableId;
    await this.defaultConvert({ command }, o, helper);

    if (this.needCreateSymmetricColumn(fromColumn, command)) {
      const {
        foreignWorkspaceId,
        foreignCoreId,
        foreignTableId,
        oneToOne,
      } = command.columnConfig as RecordReferenceColumnConfig;
      const tableName = command.column.fromTable.get()[0].name;
      const _command: CreateColumn = plainToClass(CreateColumn, {
        workspaceId: foreignWorkspaceId,
        coreId: foreignCoreId,
        tableId: foreignTableId,
        columnConfig: {
          foreignWorkspaceId: command.workspaceId,
          foreignCoreId: command.coreId,
          foreignTableId: command.tableId,
          oneToOne,
          symmetricColumnId: command.column.uid!,
          name: tableName,
          type: ColumnType.RECORD_REFERENCE,
        },
        _metadata: {
          ...command._metadata,
        },
      });
      const symmetricColumn = await this.createSymmetricColumn(_command, o);
      (command.columnConfig as RecordReferenceColumnConfig).symmetricColumnId = this.getNodeTempId(symmetricColumn);
      column.hasSymmetricColumn.add(symmetricColumn);
      symmetricColumn.hasSymmetricColumn.add(column);
      // mapping uid of column that is being updated to its symmetric column temporary id
      helper.symmetricColumnMap.set(command.column.uid!, this.getNodeTempId(symmetricColumn));
    }

    return this;
  }

  private needCreateSymmetricColumn(fromColumn: Column, command: UpdateColumn_v2) {
    const { columnConfig: toColumnConfig, tableId } = command;
    if (toColumnConfig.type === ColumnType.RECORD_REFERENCE) {
      const _toColumnConfig = toColumnConfig as RecordReferenceColumnConfig;
      if (_toColumnConfig.foreignTableId === tableId) {
        return false;
      }
      if (fromColumn.columnType !== ColumnType.RECORD_REFERENCE) {
        return true;
      }
      if (
        fromColumn.foreignWorkspaceId !== _toColumnConfig.foreignWorkspaceId ||
        fromColumn.foreignCoreId !== _toColumnConfig.foreignCoreId ||
        fromColumn.foreignTableId !== _toColumnConfig.foreignTableId
      ) {
        return true;
      }
    }
    return false;
  }

  private async createSymmetricColumn(command: CreateColumn, o: CommonOptions): Promise<Column> {
    const { tableId } = command;
    // query table to ORM
    const table = await this.tableReadService.tablesByTableId(tableId, {
      includeView: true,
      includeColumn: true,
      includeRow: true,
    });
    if (!table) throw new TableNotExistError(tableId);
    const views = table.hasView.get();
    const column: Column = this.createColumn(
      command,
      o,
      views.map(v => v),
    );
    return column;
  }

  protected addColumnUpdateEvent(
    command: UpdateColumn_v2,
    o: CommonOptions,
    updateColumnHelper: UpdateColumnHelper = {},
  ) {
    const { workspaceId, coreId, tableId, columnConfig, column, _metadata } = command;
    const { symmetricColumnId } = columnConfig as RecordReferenceColumnConfig;
    if (!symmetricColumnId) {
      column.columnType = columnConfig.type;
      const { columnId } = updateColumnHelper;
      o.events.addEvents(
        new ColumnConfigUpdated(workspaceId, coreId, tableId, column.uid ?? columnId!, columnConfig, _metadata),
        ['symmetricColumnId', ...(column.uid ? ['columnId'] : [])],
      );
    } else {
      // for reference self
      const recordReferenceColumnUpdated = new ColumnConfigUpdated(
        command.workspaceId,
        command.coreId,
        command.tableId,
        column.uid!,
        columnConfig,
        command._metadata,
      );
      o.events.addEvents(recordReferenceColumnUpdated);
    }
  }

